﻿using System.Reactive.Linq;
using System.Reflection;
using System.Threading;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Beaker.OpenCube.Render.Cameras;
using Beaker.OpenCube.Render.Sampling;
using Beaker.OpenCube.Render.Tracing;
using System;
using Color = Beaker.OpenCube.Render.Maths.Color;

namespace Beaker.OpenCube.Render
{
	public static class RayTracerExtensions
	{
		public static IObservable<IRenderUpdate> Render( this ITracer tracer, Scene scene, ViewPlane plane )
		{
			return scene.Camera.Render( scene, plane, tracer );
		}

		public static WriteableBitmap RenderWriteableBitmap( this ITracer tracer, Scene scene, ViewPlane plane )
		{
			ManualResetEventSlim signal = new ManualResetEventSlim( false );

			int width = plane.PixelWidth;
			int height = plane.PixelHeight;

			var bitmap = new WriteableBitmap( width, height, 96, 96, PixelFormats.Bgr24, null );

			// start rendering the scene onto the plane
			var observable = tracer.Render( scene, plane );
			observable
				.ObserveOnDispatcher()
				.Subscribe(
					update =>
					{
						LineRenderUpdate line = update as LineRenderUpdate;
						if( line != null )
						{
							var pixels = line.GetPixels();
							byte[] buffer = new byte[width * 3];
							int index = 0;
							foreach( Color pixel in pixels )
							{
								var c = pixel.ToDisplayColor();
								buffer[index++] = c.B;
								buffer[index++] = c.G;
								buffer[index++] = c.R;
							}
							bitmap.WritePixels( new Int32Rect( 0, line.LineNumber, width, 1 ), buffer, width * 3, 0 );
						}
					},
					error =>
					{
						throw new TargetInvocationException( error );
					},
					() =>
					{
						signal.Set();
					}
				);

			signal.Wait();
			return bitmap;
		}
	}
}
